<?php
require_once 'ccbXml.php';

class Ccbtransfer
{
    private $_xmlHandler; //xml 句柄

    private $message = '';

    private $errorCode = 0; //错误码

    //ip地址
    private $host = '202.91.246.181';

    //端口号
    private $prot = '55533';

    function __construct()
    {
        // set_include_path(APPPATH. 'libraries/ccb/conf'); 正式环境不支持
        // spl_autoload_extensions('.php,.inc');
        // spl_autoload_register();
        spl_autoload_register(array('Ccbtransfer', 'loadprint'));
    }

    private function loadprint($class)
    {
        $classes = array('abccbconf', 'ccbconf', 'conf6w0201', 'conf6w0500', 'conf6w1303', 'conf6w1503', 'conf6w2100', 'conf6w2102', 'conf6w2104', 'conf6w8010', 'conf6w8020', 'conf6w9101', 'ubankconf');
        foreach ($classes as $key => $class) {
            $file = APPPATH. 'libraries/ccb/conf/'. $class . '.php';
            if (is_file($file)) {
                require_once($file);
            }
        }
    }

    /**
     * 转账 建行转建行 
     */
    public function transfer($code, $parameter)
    {
        try {
            $conf = new Ccbconf($code, $this->getSendDataFormat($code, $parameter));
            $this->_xmlHandler = new CcbXml();
            $sendXml = $this->_xmlHandler->buildXml($conf->getXml()); //获取交易的xml
            $response = $this->buildSocket($sendXml); //建立socket 并发送xml给建行 返回xml 响应报文
            return $conf->parsesResult($response);
        } catch ( Exception $e ) {
            $this->setErrorMsg($e->getMessage());
            $this->setErrorCode($e->getCode());
            return FALSE;
        }
    }

    /**
     * 创建socket通信 账户转账
     */
    private function buildSocket($sendData)
    {
        if (($socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
            throw new Exception("socket_create() failed: reason: " . socket_strerror(socket_last_error()), 1);
        }

        socket_set_option($socket,SOL_SOCKET,SO_RCVTIMEO,array("sec"=>60, "usec"=>0 ) ); //超时 发送时间3秒
        socket_set_option($socket,SOL_SOCKET,SO_SNDTIMEO,array("sec"=>60, "usec"=>0 ) ); //超时 接受时间3秒

        if ( @socket_connect($socket, $this->host, $this->prot) == FALSE ) {
            throw new Exception("socket_connect() failed: reason: " . socket_strerror(socket_last_error()), 1);
        }

        socket_write($socket, $sendData, strlen($sendData));
        $buf = '';
        while ($out = socket_read($socket, 4096)) {
            $buf .= $out;
        }
        return $this->_xmlHandler->simplexmlLoadString($buf); //解析响应报文 修改响应报文格式为UTF8 并返回
    }

    /**
     * 建行交易 必须出入的参数
     */
    public function getSendDataFormat($code, $sendData)
    {
        $parameter = array();
        switch ($code) {
            case '6W1303': //（6W1303）单笔代发代扣交易
                $parameter = array(
                    'REQUEST_SN'        => (int)$sendData->transfer_sn, // 请求序列号, 只可以使用数字
                    'ACC_NO2'           => $sendData->bank_card, // 对方账户
                    'OTHER_NAME'        => $sendData->cardholder,  // 转入账户
                    'AMOUNT'            => $sendData->amount, //转账金额必须大于0
                    'UBANK_NO'          => $this->getUbankNo($sendData->bank_name) //对方账户支付系统行号
                );
                break;
            case '6W8010': //6W8010 自动转帐交易 建行对建  可以转账到信用卡
            case '6W8020': //6W8020 非自动转帐交易 跨行转账 可以转账到信用卡
                $parameter = array(
                    'REQUEST_SN'        => (int)$sendData->transfer_sn, // 请求序列号, 只可以使用数字
                    'RECV_ACCNO'        => $sendData->bank_card,  // 转入账户
                    'RECV_UBANKNO'      => $this->getUbankNo($sendData->bank_name), //转入账户联行号
                    'RECV_ACC_NAME'     => $sendData->cardholder, // 转入账户名称
                    'RECV_OPENACC_DEPT' => $sendData->bank_name, //请输入转入账户开户机构名称
                    'AMOUNT'            => $sendData->amount, //转账金额必须大于0
                    'USEOF'             => '网银代发'
                );
                break;
            case '6W0500': // （6W0500）转帐交易结果查询 对应6W8010， 6W8020
            case '6W1503': // （6W1503）代发代扣不确定交易查询 对应6W1303
                $parameter = array(
                    'REQUEST_SN'        => (int)$sendData->transfer_sn, // 请求序列号, 只可以使用数字
                    'REQUEST_SN1'        => $sendData->transfer_sn  // 原请求序列号
                );
                break;
            case '6W2100': // （6W2100）全国批量代发代扣交易
                $parameter = array(
                    'REQUEST_SN'    => $sendData['REQUEST_SN'], // 请求序列号, 只可以使用数字
                    'USE'           => '网银代发',  // 用途 varChar(100) 
                    'SEND_FILE'     => $sendData['SEND_FILE'], //文件上传到网银后返回的文件名
                    'ORI_FILE'      => $sendData['ORI_FILE'], //原文件名 varChar(50) F 本地文件名 
                    'ACC_NO'        => '', //代发时为付款账号，代扣时为收款账号
                    'AMOUNT'        => $sendData['AMOUNT'], //建议填写该批上传文件总金额，避免填错文件
                    'COUNT'         => $sendData['COUNT'], //建议填写该批上传文件总笔数，避免填错文件
                    'FLOW_FLAG'     => 0, //空或0.不需审批 1.网银审批
                    'ASYN_FLAG'     => 0, //0：正常处理 1：异步处理（异步处理总笔数必须大于5万）
                    'RET_ERRTYPE'   => 0, //1：一次返回批量文件中前100条错误(批量文件总笔数不能超出5000笔)0或空：按现有规则（有错误则跳出并返回错误码及错误描述）
                    'transferType'  => $sendData['transferType'], //1，建行转建行 2，建行转它行
                );
                break;
            case '6W2102': //（6W2102）全国代发代扣文件上传
                $parameter = array(
                    'REQUEST_SN'    => $sendData['REQUEST_SN'], // 请求序列号, 只可以使用数字
                    'SEND_FILE'     => $sendData['SEND_FILE'] //上传文件名
                );
                break;
            case '6W2104': //（6W2104）全国批量代发代扣结果明细查询
                $parameter = array(
                    'REQUEST_SN'    => $sendData['REQUEST_SN'], // 请求序列号, 只可以使用数字
                    'REQUEST_SN1'   => $sendData['REQUEST_SN1'], //原请求序列号
                    'START_PAGE'    => $sendData['START_PAGE'] //起始页次 Int T 整数>0,不填默认查询第一页 
                );
                break;
            case '6W9101':
            case '6W0201':
                $parameter = array(
                    'REQUEST_SN'        => (int)$sendData->transfer_sn // 请求序列号, 只可以使用数字
                );
                break;
            default:
                throw new Exception("请求交易码有误", 1);
                break;
        }
        return $parameter;
    }

    /**
     * 对方账户支付系统行号
     */
    public function getUbankNo($bankNane)
    {
        $uBank = new Ubankconf();
        $uBankConf = $uBank->getUbankNo();
        $bankNane = '1'. $bankNane;
        if (mb_strpos($bankNane, '中国银行')) {
            return 104100000004;
        } else {
            $bankNane = str_replace('中国', '', $bankNane);
            $bankNane = ltrim($bankNane, '1');
            $pos = mb_strpos($bankNane, '银行');
            if ($pos === false) {
                throw new Exception("无法识别银行名称,请填写完整的银行名称", 1);
            }
            $bankNane = mb_substr($bankNane, 0, $pos);
            $len = mb_strlen($bankNane);

            foreach ($uBankConf as $key => $value) {
                if ($bankNane == mb_substr($value, mb_strpos($value, '银行') -2, $len)) {
                    return $key;
                }
            }
            throw new Exception("获取不到银行系统行号,请填写完整的银行名称", 1);
        }
    }

    private function setErrorMsg($msg)
    {
        $this->message = $msg;
    }

    private function setErrorCode($code)
    {
        $this->errorCode = $code;
    }

    /**
     * 获取错误消息
     */
    public function getErrorMsg()
    {
        return $this->message;
    }

    public function getErrorCode()
    {
        return $this->errorCode;
    }

    /**
     * 获取建设银行 上传文件的excel路径
     */
    public function getCcbSendFIleName($name, $type = 1)
    {
        if ($type == 1) {
            $name .= '-ccb';
        } else {
            $name .= '-other';
        }
        return $name;
    }

    /**
     * 获取建设银行 上传文件的excel路径
     */
    public function getCcbSendFIlePath($name)
    {
        return 'D:\ccb\sendxls\\'. $name;
    }
}